/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* $Id: _IOUtils.java 1666014 2015-03-11 21:08:39Z tilman $ */

package com.haotian.io;

import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

/**
 * This class contains various I/O-related methods.
 */
public final class _IOUtils {

	private static Logger log = Logger.getLogger(_IOUtils.class);


	private _IOUtils() {
		// Utility class. Don't instantiate.
	}

	/**
	 * 从输入流中获取字节数组 <br>
	 * Get the byte array from the input stream
	 * 
	 * @author haotianrich@outlook.com
	 * @date 2017年7月14日
	 * @param in
	 * @return byte[]
	 * @throws IOException
	 */
	public static byte[] toByteArray(InputStream in) throws IOException {
		ByteArrayOutputStream baout = new ByteArrayOutputStream();
		copy(in, baout);
		return baout.toByteArray();
	}

	/**
	 * 从给定的输入流的所有内容复制到给定的输出 <br>
	 * Copies all the contents from the given input stream to the given output
	 * stream.
	 * 
	 * @param input
	 *            the input stream
	 * @param output
	 *            the output stream
	 * @return the number of bytes that have been copied
	 * @throws IOException
	 *             if an I/O error occurs
	 */
	public static long copy(InputStream input, OutputStream output) throws IOException {
		byte[] buffer = new byte[4096];
		long count = 0;
		int n = 0;
		while (-1 != (n = input.read(buffer))) {
			output.write(buffer, 0, n);
			count += n;
		}
		return count;
	}

	/**
	 * 使用从输入流读取的数据填充给定的缓冲区 ,如果数据不适合缓冲区, 则只读取适合缓冲区的数据。<br>
	 * 如果数据在缓冲区中不合适, 则缓冲区没有完全填充。<br>
	 * 如果返回写入长度不等于缓存长度 那么要考虑是否数据不完整 调用者需要丢去 或者reset 重新读取<br>
	 * Populates the given buffer with data read from the input stream. If the
	 * data doesn't fit the buffer, only the data that fits in the buffer is
	 * read. If the data is less than fits in the buffer, the buffer is not
	 * completely filled.
	 * 
	 * @param in
	 *            the input stream to read from
	 * @param buffer
	 *            the buffer to fill
	 * @return the number of bytes written to the buffer
	 * @throws IOException
	 *             if an I/O error occurs
	 */
	public static long populateBuffer(InputStream in, byte[] buffer) throws IOException {

		int remaining = buffer.length;// 1、100
		while (remaining > 0) {// 2、100>0 6、50>0 10、0>0=false
			int bufferWritePos = buffer.length - remaining;// 3、0 7、100-50=50

			// in.read(缓存,流中开始读取坐标,从开始坐标读取长度)
			int bytesRead = in.read(buffer, bufferWritePos, remaining);// 4、50
																		// 8、50
			// 如果小于0 到达流末尾
			if (bytesRead < 0) {
				break; // EOD
			}
			remaining -= bytesRead;// 5、100-=50 9、50-=50
		}
		// 如果返回写入长度不等于缓存长度 那么要考虑是否数据不完整 调用者需要丢去 或者reset 重新读取
		return buffer.length - remaining;// 11、 100-0=100
	}

	/**
	 * Null safe close of the given {@link Closeable} suppressing any exception.
	 * 关闭实现了 Closeable的所有流
	 * 
	 * @param closeable
	 *            to be closed
	 */
	public static void closeQuietly(Closeable closeable) {
		try {
			if (closeable != null) {
				closeable.close();
			}
		} catch (IOException ioe) {
			// ignore
		}
	}

	/**
	 * 向流中写入指定字节长度的字符串，不足时补0<br>
	 *
	 * @author haotianrich@outlook.com
	 *         <p/>
	 * @date 2017年7月14日
	 * @param os
	 *            输出流
	 * @param content
	 *            写入内容
	 * @param len
	 *            写入长度
	 * @param charset
	 *            写入编码
	 * @throws IOException
	 */
	public static void writeString(OutputStream os, String content, int len, String charset) throws IOException {
		byte[] data = null;
		if (StringUtils.isBlank(charset)) {
			charset = "ASCII";
		}
		data = content.getBytes(charset);
		if (data.length > len) {
			log.error("向流中写入的字符串超长！要写" + len + " 内容:" + content + "，长度为:" + data.length);
		}
		int srcLen = data.length;
		os.write(data);
		while (srcLen < len) {
			os.write('\0');
			srcLen++;
		}
		data = null;

	}

	/**
	 * 从流中读取指定长度的字节，转成字符串返回<br>
	 *
	 * @author haotianrich@outlook.com
	 *         <p/>
	 * @date 2017年7月14日
	 * @param in
	 * @param len
	 * @param charset
	 * @return
	 * @throws IOException
	 */
	public static String readString(InputStream in, int len, String charset) throws IOException {
		byte[] buffer = new byte[len];

		long readLen = _IOUtils.populateBuffer(in, buffer);
		// 读取的长度不足
		if (readLen < len)
			log.error("读取长度为：" + readLen + ",应读取长度:" + len);

		String s = null;
		if (StringUtils.isBlank(charset))
			s = new String(buffer);
		else
			s = new String(buffer, charset);

		if (null != s)
			s = s.trim();

		return s;
	}

	/**
	 * 从输入流中获取8字节long 类型 <br>
	 * 高位在前
	 * 
	 * @author haotianrich@outlook.com
	 *         <p/>
	 * @date 2017年7月14日
	 * @param in
	 * @return
	 * @throws IOException
	 */
	public static long readLong(InputStream in) throws IOException {
		byte[] buffer = new byte[8];
		long res = 0l;
		if (_IOUtils.populateBuffer(in, buffer) == 8) {
			try {
				return (((long) buffer[0] << 56) + ((long) (buffer[1] & 255) << 48) + ((long) (buffer[2] & 255) << 40)
						+ ((long) (buffer[3] & 255) << 32) + ((long) (buffer[4] & 255) << 24)
						+ ((buffer[5] & 255) << 16) + ((buffer[6] & 255) << 8) + ((buffer[7] & 255) << 0));
			} catch (Exception e) {
				log.error("", e);
			}
		} else {
			log.error("从流中读取long类型错误，读取长度错误");
		}
		return res;
	}

	/**
	 * 低位在前，流中读取long 类型
	 *
	 * @author haotianrich@outlook.com
	 *         <p/>
	 * @date 2017年7月14日
	 * @param in
	 * @return
	 * @throws IOException
	 */
	public static long readLongLE(InputStream in) throws IOException {
		byte[] buffer = new byte[8];
		long res = 0l;
		if (_IOUtils.populateBuffer(in, buffer) == 8) {
			try {
				return (((long) buffer[0] << 0) + ((long) (buffer[1] & 255) << 8) + ((long) (buffer[2] & 255) << 16)
						+ ((long) (buffer[3] & 255) << 24) + ((long) (buffer[4] & 255) << 32)
						+ ((buffer[5] & 255) << 40) + ((buffer[6] & 255) << 48) + ((buffer[7] & 255) << 56));
			} catch (Exception e) {
				log.error("", e);
			}
		} else {
			log.error("从流中读取long类型错误，读取长度错误");
		}
		return res;
	}
}
